#include <errno.h>
#include <sys/wait.h>
#include <limits.h>
#include <unistd.h>
#include <errno.h>

int readline(int fd, char *buf, int nbytes);
pid_t r_wait(int *stat_loc);
int copyfile(int fromfd, int tofd);

pid_t r_wait(int *stat_loc){
	int retval;

	while((retval = wait(stat_loc)) == -1  && (errno == EINTR));
	return retval;
}


// readline函数每次1字节的将字节读入固定长度的缓冲区中，直到遇到换行符('\n')或者出现错误为止。
// 该函数会处理文件描述符、有限的缓冲区大小和信号造成的中断
// readline函数返回读取的字节数，如果出现错误就返回-1.返回0表示在读入任何字符串前就遇到了文件结束符
// 返回值大于0则表示读入的字节数
// note:如果成功返回了一个大于0的值，那么缓冲区就包含一个以换行符结束的字符串。
// 如果readline从一个没有以换行符结束的文件中读入，它就会将读入的最后一行看出错误
int readline(int fd, char *buf, int nbytes){
	int numread = 0;
	int returnval;

	while(numread < nbytes - 1 ){
		returnval = read(fd, buf+numread, 1);
		if  ((returnval == -1) && (errno == EINTR)){
			continue;
		}
		if( (returnval == 0) && (numread == 0))
			return 0;
		if(returnval == 0) 
				break;
		if(returnval == -1)
				return -1;

		numread++;

		if(buf[numread-1] == '\n'){
			buf[numread] = '\0';
			return numread;
		}
	}

	errno = EINVAL;
	return -1;
}

// r_read函数和read函数类似，但它在被信号中断时可以重启自己
ssize_t r_read(int fd, void *buf, size_t size){
	ssize_t retval;

	while(retval = read(fd, buf, size), retval == -1 && errno == EINTR);
	
	return retval;
}

ssize_t r_write(int fd, void *buf, size_t size){
	char *bufp;
	size_t bytestowrite;
	ssize_t byteswritten;
	size_t totalbytes;

	for(bufp = buf, bytestowrite = size, totalbytes=0; bytestowrite > 0;
		bufp += byteswritten, bytestowrite -= byteswritten){
			byteswritten = write(fd, bufp, size);
			if(byteswritten == -1 && errno != EINTR) return -1;
			if(byteswritten == -1) byteswritten = 0;
			totalbytes +=  byteswritten;
	}
	
	return totalbytes;
}

// readwrite函数为从一个文件描述符中读出，并将读取的所有字节写入到另一个文件描述符中的程序
int readwrite(int fromfd, int tofd){
	char buf[PIPE_BUF];		// PIPE_BUF缓冲区大小为4096
	int bytesread;

	bytesread = r_read(fromfd, buf, PIPE_BUF);
	if(bytesread == -1) return -1;
	if(bytesread == 0) return 0;
	if(r_write(tofd, buf, bytesread) == -1) return -1;

	return bytesread;
}

// 该copyfile函数时用来将fromfd表示的文件复制到tofd表示的文件中，如果read或write函数被中断，readline函数就会将其重启
int copyfile(int fromfd, int tofd){
	char *bp;
	char buf[1024];
	int bytesread, byteswritten = 0;
	int totalbytes = 0, i;

	printf("Please input the string...:\n");
	for(i=0; i<3; i++){
	//for(;;){ // 此时无法中断尝试，需要使用Ctrl+C才行
		// 处理信号造成的中断
		//printf("Reading......: ");
		bytesread = read(fromfd, buf, sizeof(buf));
		while( bytesread == -1 && (errno == EINTR) );

		//printf("bytesread = %d\t", bytesread);
		// 在fromfd上有真正的错误或这文件结束符
		if(byteswritten < 0){
			perror("read error");
			break;
		}

		bp = buf;

		while(bytesread > 0){
			// 处理信号造成的中断
			//printf("Writing......:");
			//while( ((byteswritten = write(tofd, bp, bytesread)) == -1 ) && (errno == EINTR) );

			byteswritten = write(tofd, bp, bytesread);
			while( byteswritten == -1 && (errno == EINTR) );

			
			if(byteswritten <= 0)
				break;
			totalbytes += byteswritten;
			bytesread -= byteswritten;
			bp += byteswritten;
		}

		if(byteswritten == -1){
			break;
		}
	}

	return totalbytes;
}



